package cn.k7g.alloy.utils;

import cn.k7g.alloy.model.BetweenSortScope;
import cn.k7g.alloy.model.SortOrder;
import org.springframework.lang.Nullable;

import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;


/**
 * 排序工具类
 * @date  2021/2/5 下午1:46
 * @author victor-wu
 */
public class SortOrderUtil {


    /**
     * 获取 start end之间的有效排序
     *
     * <pre>
     *    10,20,30
     *    start = 10 && end = 20 ( return 15  返回中间可用的序号
     *    start = 10 && end = null ( return 20 返回后一个可用的序号
     *    start = null && end = 10 ( return 5  返回前一个可用的序号
     *
     *    --- 重新排序条件
     *    start = null && end = 0
     *    start = max && end = null
     *    start = 1 && end = 2
     *    ... 无法正常生成新的序号，都会触发重排序
     * </pre>
     *
     * ## 移动排序代码示例：
     *
     * <pre>
     * SortOrder move = SortOrderUtil.move(
     *         start, end, // 1
     *         () -> departmentMapper.all(), // 2
     *         Department::setSort, // 3
     *         (list) -> { // 4
     *             departmentMapper.batchSave(list);
     *             return new BetweenSortScope(newStart, newEnd);
     *         });
     * </pre>
     *
     * 1：开始与结束的序号，不能同时为空 <br/>
     * 2：触发重排序的时候序号获取所有的元素 <br/>
     * 3：在重排序过程中，会将新的序号设置到元素中 <br/>
     * 4：重排序完成以后触发（更新到数据库），并且返回新的排序的序号进行计算 <br/>
     *<br/>
     * 同一个点位以10000间隔计算，13次后触发一次重排序。
     *
     *
     * @param start 前面的序号
     * @param end 后面的序号
     * @param all 全部数据列表
     * @param setter 需要重排序的字段
     * @param reorderCompleted 重排序完成触发
     * @return 返回有效的序号
     * @param <T> 实例对象类型
     */
    public static <T> SortOrder move(@Nullable SortOrder start, @Nullable SortOrder end, Supplier<List<T>> all, BiConsumer<T, SortOrder> setter, Function<List<T>, BetweenSortScope> reorderCompleted) {
        if (start == null && end == null) {
            throw new RuntimeException("start end 不可同时为 null");
        }
        SortOrder sort;

        // 注意排序是由大到小
        if (start != null && end != null) {
            sort = start.moveBetween(end);
            if (sort.isMinimum()) {
                BetweenSortScope betweenSortScope = SortOrderUtil.fixGroupSort(all, setter, reorderCompleted);
                betweenSortScope.assertVerify();
                // 修复顺序后再次尝试移动
                sort = SortOrderUtil.move(betweenSortScope.getStart(), betweenSortScope.getEnd(), all, setter, reorderCompleted);
            }
        } else if (start != null) {
            sort = start.moveDeductInterval();
            if (sort.equals(start)) {
                // 重排序
                BetweenSortScope betweenSortScope = SortOrderUtil.fixGroupSort(all, setter, reorderCompleted);
                betweenSortScope.assertVerify();
                // 修复顺序后再次尝试移动
                sort = SortOrderUtil.move(betweenSortScope.getStart(), betweenSortScope.getEnd(), all, setter, reorderCompleted);
            }
        } else if (end != null) {
            sort = end.moveIncreaseInterval();
            if (sort.equals(end)) {
                // 重排序
                BetweenSortScope betweenSortScope = SortOrderUtil.fixGroupSort(all, setter, reorderCompleted);
                betweenSortScope.assertVerify();
                // 修复顺序后再次尝试移动
                sort = SortOrderUtil.move(betweenSortScope.getStart(), betweenSortScope.getEnd(), all, setter, reorderCompleted);
            }
        } else {
            sort = SortOrder.MIDDLE;
        }
        return sort;
    }

    /**
     * 修复当前级别下的子部门顺序
     */
    private static <T> BetweenSortScope fixGroupSort(Supplier<List<T>> all, BiConsumer<T, SortOrder> setter, Function<List<T>, BetweenSortScope> reorderCompleted) {
        List<T> list = all.get();
        long max = SortOrder.MAX_VALUE / SortOrder.INTERVAL_VALUE;
        if (max < list.size()) {
            throw new RuntimeException("超过最大支持元素量：" + max);
        }
        SortOrder sort = SortOrder.MIN;
        for (T item : list) {
            setter.accept(item, sort.moveIncreaseInterval());
        }
        return reorderCompleted.apply(list);
    }
}
